﻿//Copyright (c) 2007, Moq Contributors
//http://code.google.com/p/moq-contrib/
//All rights reserved.

//Redistribution and use in source and binary forms, 
//with or without modification, are permitted provided 
//that the following conditions are met:

//    * Redistributions of source code must retain the 
//    above copyright notice, this list of conditions and 
//    the following disclaimer.

//    * Redistributions in binary form must reproduce 
//    the above copyright notice, this list of conditions 
//    and the following disclaimer in the documentation 
//    and/or other materials provided with the distribution.

//    * Neither the name of the Moq Contributors nor the 
//    names of its contributors may be used to endorse 
//    or promote products derived from this software 
//    without specific prior written permission.

//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
//CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
//INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
//MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
//CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
//SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
//INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
//WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
//OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
//SUCH DAMAGE.

//[This is the BSD license, see
// http://www.opensource.org/licenses/bsd-license.php]

using System;
using System.Collections.Generic;
using System.Reflection;

using Autofac.Builder;
using Autofac.Core;

namespace Moq.Contrib.Indy
{
	/// <summary> 
	/// Resolves unknown interfaces and Mocks using 
	/// the <see cref="MockRepository"/> from the scope. 
	/// </summary>
	public class MoqRegistrationSource : IRegistrationSource
	{
		private readonly MethodInfo _createMethod;
		private readonly MockRepository _repository;

		/// <summary>
		/// Initializes a new instance of the <see cref="MoqRegistrationSource"/> class.
		/// </summary>
		/// <param name="repository">The repository.</param>
		public MoqRegistrationSource(MockRepository repository)
		{
			_repository = repository;
			_createMethod = GetType().GetMethod("CreateUsingRepository", BindingFlags.NonPublic | BindingFlags.Instance).GetGenericMethodDefinition();
		}

		#region IRegistrationSource Members

		/// <summary>
		/// Retrieve registrations for an unregistered service, to be used
		/// by the container.
		/// </summary>
		/// <param name="service">The service that was requested.</param>
		/// <param name="registrationAccessor">A function that will return existing registrations for a service.</param>
		/// <returns>
		/// Registrations providing the service.
		/// </returns>
		public IEnumerable<IComponentRegistration> RegistrationsFor(Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
		{
			if (service == null) throw new ArgumentNullException("service");

			var typedService = service as TypedService;
			if ((typedService == null) || (!typedService.ServiceType.IsInterface))
				yield break;

			yield return RegistrationBuilder
				.ForDelegate((context, parameters) =>
				{
					MethodInfo typedMethod = _createMethod.MakeGenericMethod(new[] { typedService.ServiceType });
					var mock = (Mock) typedMethod.Invoke(this, null);
					return mock.Object;
				})
				.As(typedService)
				.SingleInstance()
				.CreateRegistration();
		}

		/// <summary>
		/// Gets whether the registrations provided by this source are 1:1 adapters on top
		/// of other components (I.e. like Meta, Func or Owned.)
		/// </summary>
		public bool IsAdapterForIndividualComponents
		{
			get { return false; }
		}

		#endregion

		// ReSharper disable UnusedMember.Local
		/// <summary>
		/// Creates the desired mock using repository logic. This abstraction exists to simplify the process of using
		/// reflection to call a generic method; the repository Create method has several overloads that would cause
		/// the resulting reflection code to be more complex.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		private Mock<T> CreateUsingRepository<T>() where T : class
		{
			return _repository.Create<T>();
		}
		// ReSharper restore UnusedMember.Local
	}
}